package net.awpspace.relaychat.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * Client executable
 * 
 * @author Diep Hoang Savvycom JSC
 * 
 */
public class Client {

	/**
	 * Initialization command message
	 */
	private static final String MSG_INIT = "remote_chat ";

	/**
	 * Client socket
	 */
	private static Socket socket;

	/**
	 * Client output stream
	 */
	private static OutputStream outputStream;

	/**
	 * Client input stream
	 */
	private static InputStream inputStream;

	/**
	 * Application entry point
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("Client started...");
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		/*
		 * Wait until user enter right command: CONNECTION_SETUP
		 */
		while (true) {
			String cmd = null;

			// remove spaces
			try {
				cmd = br.readLine().trim().replaceAll(" +", " ");
			} catch (IOException e) {
				System.out.println("IO error: " + e.getMessage());
			}

			// check init command
			if (!cmd.startsWith(MSG_INIT)) {
				System.out.println("Invalid command");
				continue;
			}

			// concat parameters
			cmd = cmd.substring(MSG_INIT.length());
			String[] params = cmd.split(" ");

			// parameters must include two sections
			if (params.length != 2) {
				System.out.println("Invalid parameters " + cmd);
				continue;
			}

			// first section is server hostname address
			String[] severParams = params[0].split(":");
			if (severParams.length != 2) {
				System.out.println("Invalid parameters");
				continue;
			}
			String serverIpAddress = severParams[0];
			int serverPort = -1;
			try {
				serverPort = Integer.parseInt(severParams[1]);
			} catch (NumberFormatException e) {
				System.out.println("Invalid parameters");
				continue;
			}

			// second section is friend hostname address
			String[] friendParams = params[1].split(":");
			if (friendParams.length != 2) {
				System.out.println("Invalid parameters");
				continue;
			}
			String friendIpAddress = friendParams[0];
			int friendPort;
			try {
				friendPort = Integer.parseInt(friendParams[1]);
			} catch (NumberFormatException e) {
				System.out.println("Invalid parameters");
				continue;
			}

			// parameters are valid, send CONNECTION_SETUP message
			System.out.println("Connecting to the server...");
			try {
				socket = new Socket(serverIpAddress, serverPort);
			} catch (UnknownHostException e) {
				System.out.println("Can not find server");
				continue;
			} catch (IOException e) {
				System.out.println("Network error: " + e.getMessage());
				continue;
			}
			System.out.println("Connected to server: " + serverIpAddress + ':'
					+ serverPort);
			try {
				inputStream = socket.getInputStream();
				outputStream = socket.getOutputStream();
			} catch (IOException e) {
			}

			// connected to the server, now send DATA_CONNECTION message
			System.out.println("Requesting client...");
			String[] ipNodes = friendIpAddress.split(".");

			/*
			 * First byte: message detection flag Next 4 bytes: client host name
			 * Next 2 bytes: client host port
			 */
			byte[] connectionSetup = new byte[ipNodes.length + 3];
			connectionSetup[0] = 0;
			try {
				for (int i = 0; i < ipNodes.length; ++i) {
					connectionSetup[i + 1] = (byte) Integer
							.parseInt(ipNodes[i]);
				}
			} catch (NumberFormatException e) {
				System.out.println("Invalid parameters");
				try {
					socket.close();
				} catch (IOException ex) {
				}
				continue;
			}
			connectionSetup[ipNodes.length + 1] = (byte) (friendPort >> 8);
			connectionSetup[ipNodes.length + 2] = (byte) friendPort;

			// send message
			try {
				outputStream.write(connectionSetup);
			} catch (IOException e) {
				System.out.println("Request IO error: " + e.getMessage());
				try {
					socket.close();
				} catch (IOException ex) {
				}
				continue;
			}

			// connection success
			System.out.println("Connection sent, type message and enter");
			break;
		}

		// start new thread which receive message
		new Thread() {

			@Override
			public void run() {
				byte[] buffer = new byte[1024];
				while (true) {
					try {
						while (inputStream.read(buffer) > 0) {
							System.out
									.println("Received: " + buffer.toString());
						}
					} catch (IOException e) {
					}
				}
			}
		}.start();

		// main thread will send message
		switchToChatCommand(br);
	}

	/**
	 * Get message from user and send DATA_EXCHANGE message
	 * 
	 * @param br
	 */
	private static void switchToChatCommand(BufferedReader br) {
		Runtime.getRuntime().addShutdownHook(new Thread() {

			@Override
			public void run() {
				Client.interrupt();
			}
		});
		while (true) {
			String msg = null;
			try {
				msg = br.readLine();
			} catch (IOException e) {
			}
			if (msg == null) {
				continue;
			}
			try {
				outputStream.write((byte) 1);
				outputStream.write(msg.getBytes());
			} catch (IOException e) {
				System.out.println("IO error: " + e.getMessage());
			}
		}
	}

	/**
	 * Send CONNECTION_TEARDOWN and terminate
	 */
	private static void interrupt() {
		System.out.println("Terminating...");
		try {
			outputStream.write(2);
		} catch (IOException e) {
		}
	}
}
